/******************************************************************************

Copyright (c) 2013-2014, Altera Corporation.  All rights reserved.

Redistribution and use of this software, in source and binary code forms, with or without modification, are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.

3. Neither the name of Altera Corporation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF THE COPYRIGHT HOLDERS OR CONTRIBUTORS ARE ADVISED OF THE POSSIBILITY DAMAGE IS LIKELY TO OCCUR.
******************************************************************************/

#include "alt_printf.h"
#include "core.h"
#include "reset.h"
#include "cache.h"
#include "neon.h"
#include "scu.h"
#include "log_buffer.h"
#include "trustzone.h"
#include "mempool.h"
#include "Layout.h"
bufferInfo _LOG0;
FILE *LOG0=(FILE *)&_LOG0;

void cpu1_main(void);

int cpu0_main(void)
{
  uint32 clrmem;

  /*****************************************
    Step 1 - Initialize the Shared Memory 
  *****************************************/
  clrmem = System_Shared_Comm_Channel_Address;
  while(clrmem < System_Shared_Comm_Channel_Address + 0x1000)
                //System_Shared_Comm_Channel_AddrEnd)
  {
    *((uint32 *)clrmem) = 0;
    clrmem += 4;
  }

  /*****************************************
    Step 2 - Split the L2 Cache
  *****************************************/
  // disable l2 cache to CPU0 initially. Linux will need to turn it on when it is ready
  alt_cache_l2_set_locks(CPU0_INSTRUCTION | CPU0_DATA, WAY0_7);

  // CPU1 instructions will go into WAY0 or WAY1
  alt_cache_l2_set_locks(CPU1_INSTRUCTION, WAY2|WAY3|WAY4_7);

  // Don't let CPU1's data affect the instructions cached
  alt_cache_l2_set_locks(CPU1_DATA, WAY0);

  // enable l2 cache but not the L1
  alt_cache_l2_init();
  alt_cache_l2_prefetch_enable();
  alt_cache_l2_parity_enable();
  alt_cache_l2_enable();

  enable_scu();

  // Note: Although the l2 cache is enabled, instructions and data will not be cached unless
  // they are in virtual memory with the pages marked as cachable. When the MMU is turned off
  // it will not store any entries to the l2 cache.

  printf("Baremetal merged AMP app v 1.2 \n");

  /*****************************************
    Step 3 - Set devices for either Linux or BM App
  *****************************************/
  // Set Devices to be accessible to Non-Trusted OS (Linux) 
  alt_l3_secgrp_set_peripheral_access(
        ALT_L3_PERIPHERAL_GLOBALTIMER | ALT_L3_PERIPHERAL_PRIVATETIMER | 
        ALT_L3_PERIPHERAL_DMA | 
        ALT_L3_PERIPHERAL_USB0 | ALT_L3_PERIPHERAL_USB1 | 
        ALT_L3_PERIPHERAL_CAN0 | ALT_L3_PERIPHERAL_CAN1 | 
        ALT_L3_PERIPHERAL_SDMMC | ALT_L3_PERIPHERAL_NAND |
        ALT_L3_PERIPHERAL_QSPI | 
        ALT_L3_PERIPHERAL_SPI0_SLAVE | ALT_L3_PERIPHERAL_SPI1_SLAVE | 
        ALT_L3_PERIPHERAL_SPI0_MASTER | ALT_L3_PERIPHERAL_SPI1_MASTER | 
        ALT_L3_PERIPHERAL_I2C0 | ALT_L3_PERIPHERAL_I2C1 | ALT_L3_PERIPHERAL_I2C2 | ALT_L3_PERIPHERAL_I2C3 | 
        ALT_L3_PERIPHERAL_EMAC0 | ALT_L3_PERIPHERAL_EMAC1 | 
        ALT_L3_PERIPHERAL_UART0 | ALT_L3_PERIPHERAL_UART1 | 
        ALT_L3_PERIPHERAL_SPTIMER0 | ALT_L3_PERIPHERAL_SPTIMER1 | 
        ALT_L3_PERIPHERAL_OSCTIMER0 | // NOT OSCTIMER1 - we want this available for our app
        ALT_L3_PERIPHERAL_WATCHDOG0 | ALT_L3_PERIPHERAL_WATCHDOG1 |
        ALT_L3_PERIPHERAL_CLKMGR | ALT_L3_PERIPHERAL_OCR | 
        ALT_L3_PERIPHERAL_ACP | ALT_L3_PERIPHERAL_ROM | 
        ALT_L3_PERIPHERAL_SDRAMCTRL | ALT_L3_PERIPHERAL_DAP | 
        ALT_L3_PERIPHERAL_RESET | ALT_L3_PERIPHERAL_SYSMGR | 
        ALT_L3_PERIPHERAL_SCANMGR | ALT_L3_PERIPHERAL_STMSLAVE |
        ALT_L3_PERIPHERAL_FPGA | ALT_L3_PERIPHERAL_FPGAMNGR
        , 0);

  // We're going to set the interrupt target at this point. Although normally the baremetal
  // app or the OS running on CPU1 would do this, they normally claim ALL interrupts. Since
  // CPU1 is running in trusted mode we are unable to prevent them from doing this. Therefore
  // we are doing it at this point because we know which devices are mapped to cpu1 at this point
  alt_int_dist_target_set_for_device(
        ALT_L3_PERIPHERAL_GPIO0 | ALT_L3_PERIPHERAL_GPIO1 | ALT_L3_PERIPHERAL_GPIO2 |
        ALT_L3_PERIPHERAL_PRIVATETIMER, 1);

  /*****************************************
    Step 4 - Set devices for either Linux or BM App
  *****************************************/
  // Must be done before SetupTZMemAccess because address 0 needs to be used
  wakeup_CPU(1, cpu1_main);

  // This function was generated from layout.py and is defined in Layout.c
  SetupTZMemAccess();

  printf("Starting Linux on Core 0....\n\n");

  start_nonsecure(Linux_Executable_Address, cpu0_TZ_Stack_End);

  return 0;
}

int main(void);

void cpu1_main(void)
{
    uint32_t *tb_sparrow = NULL;
    MemPool pool;
    __enable_neon();

    /***********************************
         Setup the MMU
    ***********************************/
    // These TLB addresses were allocated in layout.py 
    // which created these variable in Layout.h
    MemPoolInit(&pool,
      sparrow_TLB_for_sparrow_Address,
      sparrow_TLB_for_sparrow_Size);

    // This function creates the TLB in memory based on a
    // set of regions. These regions were defined in 
    // layout.py which generated Layout.h
    alt_mmu_va_space_create(&tb_sparrow, sparrow_regions, 
      sizeof(sparrow_regions)/sizeof(sparrow_regions[0]), 
      MemPoolAlloc, &pool);
    // set permissive access to memory for all domains
    alt_mmu_DACR_set(g_domain_ap, 16);
    // Set TTBR0 on, TTBR1 off, using bits 31:14
    alt_mmu_TTBCR_set(true, false, 0);
    // Set TTBR0
    alt_mmu_TTBR0_set(tb_sparrow);
    // enable the MMU
    alt_mmu_enable();

    /***********************************
         MMU setup complete
    ***********************************/
    alt_cache_l1_enable_all();

    init_log_buffer(LOG0, (void *) sparrow_OS1_s_Log_Buffer_Address, sparrow_OS1_s_Log_Buffer_Size);

    fprintf(LOG0, "Linux_Baremetal App 1.2\n");

    main();
}

/******************************************************************
    smc_handler- when an untrusted OS makes a Secure monitor call,
      a special interrupt occures which causes Sparrow to call this
      function in the user application. Because non-trusted OS's
      cannot enable/disable the l2 cache, this mechanism is used
      in place of the usual L2 enable/disable code. So, this
      function will handle the enabling/disabling of caches for
      untrusted OS's. It can also be used for other purposes as
      well, such as allowing the developer to have code here that
      executes in secure mode, such as triggering interrupts that
      are otherwise unavailable to non-trusted OS's
        These constants defined here must match code in your
      untrusted OS. Linux is provided as a sample
******************************************************************/
#define TRIGGER_INT             1
#define DISABLE_L2_CACHE_CPU0   2
#define ENABLE_L2_CACHE_CPU0    3

void smc_handler(int Reason)
{
  switch(Reason)
  {
    case TRIGGER_INT:
      break;
    case DISABLE_L2_CACHE_CPU0:
      //   disable CPU0's access to all ways. Note that the L2 cache is still
      // enabled and CPU1 is uneffected by this call
      alt_cache_l2_set_locks(CPU0_INSTRUCTION | CPU0_DATA, WAY0_7);
      break;
    case ENABLE_L2_CACHE_CPU0:
      // enable CPU0's access to L2 ways 2-7. CPU1 has sole access to ways 0-1
      alt_cache_l2_set_locks(CPU0_INSTRUCTION | CPU0_DATA, WAY0 | WAY1);
      break;
  }
}

